//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<database.cpp>
///	@path	~/src/database/
///	@date	2007/11/29
///	@desc	Database identifier lib.

#include "config/config.h"

#include "lib/utilities/dict.h"
#include "lib/system/lock.h"

#include "database/database.h"

namespace xeres {

	namespace
	{
		/*!
			\class	_IdentifierManager
			\brief	Manages database identifier.
		*/
		class _IdentifierManager
		{
		public:

			// Identifier storage
			struct _IdStore
			{
				volatile int	refCount;
				bool			enableFree;
				bool			spare[3];
				wchar_t			id[1];
			};

			static inline _IdStore * id2store( Identifier id )
			{
				return (_IdStore*)( ((byte*)id) - ( offsetof(_IdStore,id) ) );
			}

			// instance
			static _IdentifierManager& instance( void )
			{
				static _IdentifierManager s_identifierManager;
				return s_identifierManager;
			}

			// ctor
			_IdentifierManager( void )
			{
			}

			// dtor
			~_IdentifierManager( void )
			{
				dict<WString , _IdStore*>::iterator it = m_idMap.begin() , end = m_idMap.end() , next = it;
				while( it != end )
				{
					next = it;
					++next;
					_IdStore * ids = it->second;
					ids->enableFree = true;
					if( atom_xchg_add( &(ids->refCount) , 0 ) == 0 )
					{
						free( ids );
						m_idMap.erase(it);
					}
					it = next;
				}
			}

			// query
			Identifier QueryIdentifier( const WString& str )
			{
				LockGuard<ThreadLock> s_opGuard( m_lock );

				dict<WString , _IdStore*>::iterator it = m_idMap.find( str );
				if( it == m_idMap.end() )
					return NULL;

				_IdStore * ids = it->second;
				if( ids->refCount == 0 )
					return NULL;

				atom_inc( &(ids->refCount) );
				return static_cast<Identifier>( ids->id );
			}

			// query a serial of keys
			bool QueryIdentifiers( const StringSeq& strs , std::vector<Key>& keys )
			{
				LockGuard<ThreadLock> s_opGuard( m_lock );

				for( size_t i = 0 , total = strs.size() ; i < total ; ++i )
				{
					dict<WString , _IdStore*>::iterator it = m_idMap.find( strs[i] );
					if( it == m_idMap.end() )
						return false;
					_IdStore * ids = it->second;
					atom_inc(&(ids->refCount));
					keys.push_back( Key::KeyPass( static_cast<Identifier>( ids->id ) ) );
				}
				return true;
			}

			// register
			Identifier RegisterIdentifier( const WString& str )
			{
				LockGuard<ThreadLock> s_opGuard( m_lock );

				dict<WString , _IdStore*>::iterator it = m_idMap.find( str );
				if( it != m_idMap.end() )
				{
					_IdStore * ids = it->second;
					atom_inc(&(ids->refCount));
					return static_cast<Identifier>( ids->id );
				}

				// create new entry
				size_t len = sizeof(_IdStore);
				size_t strsize = str.size() * sizeof(wchar_t);
				len += strsize;
				_IdStore * ids = static_cast<_IdStore*>( malloc( len ) );
				ids->refCount = 1;
				ids->enableFree = false;
				memcpy( ids->id , str.c_str() , strsize + sizeof(wchar_t) );
				m_idMap[ str ] = ids;
				return ids->id;
			}

			// add ref
			Identifier RefIdentifier( Identifier id )
			{
				assert(id);
				_IdStore * ids = id2store(id);
				atom_inc( &(ids->refCount) );
				return id;
			}

			// release ref
			void ReleaseIdentifier( Identifier id )
			{
				assert(id);
				_IdStore * ids = id2store(id);
				if( atom_xchg_add( &(ids->refCount) , -1 ) == 0 && ids->enableFree )
				{
					free( ids );
				}
			}

		private:

		//@ data

			ThreadLock					m_lock;
			dict<WString , _IdStore*>		m_idMap;
		};
	}

	// Key
	Key::Key( const wchar_t * str )
	{
		m_key = _IdentifierManager::instance().RegisterIdentifier( str );
	}

	// Key
	Key::Key( const Key& rhs )
	{
		if( rhs.m_key )
			m_key = _IdentifierManager::instance().RefIdentifier( rhs.m_key );
		else
			m_key = NULL;
	}

	// Key
	Key::Key( const WString& str )
	{
		m_key = _IdentifierManager::instance().RegisterIdentifier( str.c_str() );
	}

	// Key
	Key::Key( const KeyPass& pass )
		: m_key( pass.m_id )
	{
		pass.m_id = NULL;
	}
	// ~Key
	Key::~Key( void )
	{
		if( IsValid() )
			_IdentifierManager::instance().ReleaseIdentifier( m_key );
	}

	Key& Key::operator = ( const Key& value )
	{
		if( m_key == NULL )
		{
			if( value.m_key )
				m_key = _IdentifierManager::instance().RefIdentifier( value.m_key );
		}
		else
		{
			TRACE_ERROR( _S("Key::operator =: Key has valid key and cannot assign another key.") );
		}
		return *this;
	}

	// QueryKey
	Key::KeyPass Key::QueryKey( const WString& name )
	{
		return KeyPass( _IdentifierManager::instance().QueryIdentifier( name ) );
	}

	// QueryKeys
	bool Key::QueryKeys( const StringSeq& names , std::vector<Key>& keys )
	{
		return _IdentifierManager::instance().QueryIdentifiers( names , keys );
	}

	// MakeKey
	Key::KeyPass Key::MakeKey( const WString& name )
	{
		return KeyPass( _IdentifierManager::instance().RegisterIdentifier( name ) );
	}

	// ~KeyPass
	Key::KeyPass::~KeyPass( void )
	{
		if( m_id != NULL )
		{
			_IdentifierManager::instance().ReleaseIdentifier( m_id );
		}
	}

} // namespace xeres
